package org.hawk.gwt.ppc.parser.impl;

import java.io.InputStream;
import java.util.List;

import junit.framework.TestCase;

import org.hawk.gwt.ppc.parser.JavaParser;
import org.hawk.gwt.ppc.parser.MethodDescriptor;
import org.hawk.gwt.ppc.parser.impl.mock.AnnotationMock1;
import org.hawk.gwt.ppc.parser.impl.mock.InterfaceMock;
import org.hawk.gwt.ppc.parser.impl.mock.MethodsMock;
import org.hawk.gwt.ppc.utils.Name;
import org.hawk.gwt.ppc.utils.PreprocessorException;

public class MethodDescriptorTestCase extends TestCase {

	public void testVoidMethod() throws PreprocessorException {
		MethodDescriptor m = getMethods(MethodsMock.class.getName()).get(0);
		
		assertNotNull(m);
		assertEquals(0, m.getParameters().size());
		assertEquals(2, m.getModifiers().size());
		assertEquals("doSomeWork", m.getName());
		assertEquals("public", m.getModifiers().get(0));
		assertEquals("final", m.getModifiers().get(1));
		assertEquals(0, m.getExceptionTypes().size());
		assertFalse(m.isVarArgs());
		assertNotNull(m.getBody());
		assertEquals(0, m.getAnnotations().size());
		assertEquals("void", m.getReturnType());
		assertEquals("", m.getTypeParameters());
		assertNull(m.getDefault());
	}
	
	public void testParameters() throws PreprocessorException {
		MethodDescriptor m = getMethods(MethodsMock.class.getName()).get(1);
		
		assertNotNull(m);
		assertEquals("doSomeWork", m.getName());
		assertEquals(2, m.getParameters().size());
		
		assertEquals("first", m.getParameters().get(0).getName());
		assertEquals("String", m.getParameters().get(0).getType());
		assertEquals(0, m.getParameters().get(0).getAnnotations().size());
		assertEquals(0, m.getParameters().get(0).getModifiers().size());
		
		assertEquals("second", m.getParameters().get(1).getName());
		assertEquals("String", m.getParameters().get(1).getType());
		assertEquals(0, m.getParameters().get(1).getAnnotations().size());
		assertEquals(0, m.getParameters().get(1).getModifiers().size());
		
		assertEquals(0, m.getModifiers().size());
		assertEquals(0, m.getExceptionTypes().size());
		assertFalse(m.isVarArgs());
		assertEquals(0, m.getAnnotations().size());
		assertNotNull(m.getBody());
		assertEquals("int", m.getReturnType());
		assertEquals("", m.getTypeParameters());
		assertNull(m.getDefault());
	}
	
	public void testTypeParameters() throws PreprocessorException {
		MethodDescriptor m = getMethods(MethodsMock.class.getName()).get(2);
		
		assertNotNull(m);
		assertEquals("getT", m.getName());
		assertEquals(1, m.getParameters().size());
		
		assertEquals("dflt", m.getParameters().get(0).getName());
		assertEquals("T", m.getParameters().get(0).getType());
		assertEquals(0, m.getParameters().get(0).getAnnotations().size());
		assertEquals(1, m.getParameters().get(0).getModifiers().size());
		assertEquals("final", m.getParameters().get(0).getModifiers().get(0));
		
		assertEquals(1, m.getModifiers().size());
		assertEquals("protected", m.getModifiers().get(0));
		assertEquals(0, m.getExceptionTypes().size());
		assertFalse(m.isVarArgs());
		assertNotNull(m.getBody());
		assertEquals(0, m.getAnnotations().size());
		assertEquals("T", m.getReturnType());
		assertEquals("<T extends CharSequence>", m.getTypeParameters());
		assertNull(m.getDefault());
	}
	
	public void testExceptions() throws PreprocessorException {
		MethodDescriptor m = getMethods(MethodsMock.class.getName()).get(3);
		
		assertNotNull(m);
		assertEquals(0, m.getParameters().size());
		assertEquals(0, m.getModifiers().size());
		assertEquals("throwError", m.getName());
		
		assertEquals(3, m.getExceptionTypes().size());
		assertEquals("java.io.IOException", m.getExceptionTypes().get(0));
		assertEquals("PreprocessorParseException", m.getExceptionTypes().get(1));
		assertEquals("InvocationTargetException", m.getExceptionTypes().get(2));
		
		assertFalse(m.isVarArgs());
		assertNotNull(m.getBody());
		assertEquals(0, m.getAnnotations().size());
		assertEquals("void", m.getReturnType());
		assertEquals("", m.getTypeParameters());
		assertNull(m.getDefault());
	}
	
	public void testAnnotations() throws PreprocessorException {
		MethodDescriptor m = getMethods(AnnotationMock1.class.getName()).get(0);
		
		assertNotNull(m);
		assertEquals(0, m.getParameters().size());
		assertEquals(0, m.getModifiers().size());
		assertEquals("x", m.getName());
		
		assertEquals(0, m.getExceptionTypes().size());
		
		assertFalse(m.isVarArgs());
		assertNull(m.getBody());
		assertEquals("int", m.getReturnType());
		assertEquals("", m.getTypeParameters());
		assertNotNull(m.getDefault());
		
		m = getMethods("org.hawk.gwt.ppc.parser.impl.mock.AnnotationMock2").get(0);
		
		assertNotNull(m);
		assertEquals(0, m.getParameters().size());
		assertEquals(0, m.getModifiers().size());
		assertEquals("value", m.getName());
		
		assertEquals(0, m.getExceptionTypes().size());
		
		assertFalse(m.isVarArgs());
		assertEquals(0, m.getAnnotations().size());
		assertNull(m.getBody());
		assertEquals("String", m.getReturnType());
		assertEquals("", m.getTypeParameters());
		assertNull(m.getDefault());
	}
	
	public void testAnnotated() throws PreprocessorException {
		MethodDescriptor m = getMethods(MethodsMock.class.getName()).get(4);
		
		assertNotNull(m);
		assertEquals("annotated", m.getName());
		assertEquals(2, m.getParameters().size());
		
		assertEquals("name", m.getParameters().get(0).getName());
		assertEquals("String", m.getParameters().get(0).getType());
		assertEquals(1, m.getParameters().get(0).getAnnotations().size());
		assertEquals(1, m.getParameters().get(0).getModifiers().size());
		assertEquals("final", m.getParameters().get(0).getModifiers().get(0));
		
		assertEquals("type", m.getParameters().get(1).getName());
		assertEquals("int", m.getParameters().get(1).getType());
		assertEquals(2, m.getParameters().get(1).getAnnotations().size());
		assertEquals(1, m.getParameters().get(1).getModifiers().size());
		assertEquals("final", m.getParameters().get(1).getModifiers().get(0));
		
		assertEquals(0, m.getModifiers().size());
		assertEquals(0, m.getExceptionTypes().size());
		assertEquals(2, m.getAnnotations().size());
		assertFalse(m.isVarArgs());
		assertNotNull(m.getBody());
		assertEquals("java.lang.Class<? extends List<String>>", m.getReturnType());
		assertEquals("", m.getTypeParameters());
		assertNull(m.getDefault());
	}
	
	public void testAbstract() throws PreprocessorException {
		MethodDescriptor m = getMethods(MethodsMock.class.getName()).get(5);
		
		assertNotNull(m);
		assertEquals("getInt", m.getName());
		assertEquals(0, m.getParameters().size());
		
		assertEquals(2, m.getModifiers().size());
		assertEquals("public", m.getModifiers().get(0));
		assertEquals("abstract", m.getModifiers().get(1));
		
		assertEquals(0, m.getExceptionTypes().size());
		assertEquals(0, m.getAnnotations().size());
		assertFalse(m.isVarArgs());
		assertNull(m.getBody());
		assertEquals("int", m.getReturnType());
		assertEquals("", m.getTypeParameters());
		assertNull(m.getDefault());
	}
	
	public void testInterface() throws PreprocessorException {
		MethodDescriptor m = getMethods(InterfaceMock.class.getName()).get(0);
		
		assertNotNull(m);
		assertEquals("value", m.getName());
		assertEquals(0, m.getParameters().size());
		
		assertEquals(0, m.getModifiers().size());
		
		assertEquals(0, m.getExceptionTypes().size());
		assertFalse(m.isVarArgs());
		assertNull(m.getBody());
		assertEquals("String", m.getReturnType());
		assertEquals("", m.getTypeParameters());
		assertNull(m.getDefault());
	}
	
	public void testVarArgs() throws PreprocessorException {
		MethodDescriptor m = getMethods(MethodsMock.class.getName()).get(6);
		
		assertNotNull(m);
		assertEquals("getSize", m.getName());
		assertEquals(2, m.getParameters().size());
		
		assertEquals("message", m.getParameters().get(0).getName());
		assertEquals("String", m.getParameters().get(0).getType());
		assertEquals(0, m.getParameters().get(0).getAnnotations().size());
		assertEquals(0, m.getParameters().get(0).getModifiers().size());
		
		assertEquals("collection", m.getParameters().get(1).getName());
		assertEquals("Object[]", m.getParameters().get(1).getType());
		assertEquals(0, m.getParameters().get(1).getAnnotations().size());
		assertEquals(0, m.getParameters().get(1).getModifiers().size());
		
		assertEquals(1, m.getModifiers().size());
		assertEquals("public", m.getModifiers().get(0));
		
		assertEquals(0, m.getExceptionTypes().size());
		assertEquals(0, m.getAnnotations().size());
		assertTrue(m.isVarArgs());
		assertNotNull(m.getBody());
		assertEquals("int", m.getReturnType());
		assertEquals("", m.getTypeParameters());
		assertNull(m.getDefault());
	}
	
	private List<MethodDescriptor> getMethods(String className) throws PreprocessorException {
		InputStream cu = getClass().getClassLoader().getResourceAsStream(Name.javaToFs(className));
		try {
			JavaParser parser = JavaParser.create(cu);
			return parser.parse().getTopClasses().get(0).getMethods();
		} finally {
			try {
				cu.close();
			} catch(Exception ex) {
			}
		}
	}
}
